package com.harry.decoupling.impl.module;

import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;

import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.BeanUtils;

import com.harry.basis.BaseGeneric;
import com.harry.basis.BasePoEntity;
import com.harry.decoupling.defi.module.GenericDao;
import com.harry.decoupling.defi.module.GenericSrv;
import com.harry.decoupling.defi.tool.ThreadPoolManager;
import com.harry.utility.BeanCtxUtils;
import com.harry.utility.InnerUtils;
import com.harry.utility.query.InnerCriteria;
import com.harry.utility.query.InnerProjection;
import com.harry.utility.query.PageCount;

public abstract class GenericSrvImpl<T extends BasePoEntity> extends BaseGeneric<T> implements GenericSrv<T> {

	@Override
	public T one(String id) throws Exception {
		return entityDao().expandEntityById(id);
	}

	@Override
	public T save(T t) throws Exception {
		return entityDao().saveEntity(t);
	}

	@Override
	public int updateByCriteria(Map<String, Object> contains, InnerCriteria cri) throws Exception {
		return entityDao().updateByCriteria(contains, cri);
	}

	@Override
	public List<T> queryEntitiesByCriteriaWithPage(InnerCriteria criteria, PageCount page) throws Exception {
		return entityDao().query(criteria, page);
	}

	@Override
	public void delete(T obj) throws Exception {
		entityDao().delete(obj);
	}

	@Override
	public int deleteByCriteria(InnerCriteria cri) throws Exception {
		ThreadPoolManager tm = BeanCtxUtils.getBeanByType(ThreadPoolManager.class);
		String[] ids = tm.submitTask(() -> {
			InnerCriteria queryCri = new InnerCriteria(cri.criterions);
			queryCri.projections = ArrayUtils.toArray(new InnerProjection(null, "id"));
			return InnerUtils.getQueryListFirstFieldStrings(entityDao().query(queryCri, null));
		});
		if (ids.length == 0) {
			return 0;
		} else {
			return entityDao().deleteByCriteria(new InnerCriteria("in", "id", (Object) ids));
		}
	}

	@Override
	public int countByCriteria(InnerCriteria criteria) throws Exception {
		return entityDao().countByCriteria(criteria);
	};

	private EntityManager em() {
		return BeanCtxUtils.getBeanByType(EntityManager.class);
	}

	@Override
	public T findByName(String nameValue) throws Exception {
		if (getNameFieldName() == null) {
			return null;
		} else {
			String tableName = InnerUtils.toUnderScoreCase(getEntityClass().getSimpleName());
			String columnName = InnerUtils.toUnderScoreCase(getNameFieldName());
			List<?> list = em().createNativeQuery(String.format("select id from %s where %s = ?", tableName, columnName)).setParameter(1, nameValue).getResultList();
			if (list.size() == 1) {
				return one((String) list.get(0));
			} else if (list.size() == 0) {
				return null;
			} else {
				throw new Exception("Mutiple "//
						+ simpleEntityName(getEntityClass().getSimpleName())// e
						+ " has the same " + simpleEntityName(getNameFieldName())// e
						+ ":" + nameValue + ".");
			}
		}
	}

	private static String simpleEntityName(String name) {
		name = name.substring(1);
		boolean firstUpper = false;
		String newName = "";
		for (int i = 0; i < name.length(); i++) {
			if (!firstUpper) {
				if (Character.isUpperCase(name.charAt(i))) {
					firstUpper = true;
					newName = newName + (name.charAt(i) + "").toLowerCase();
				}
			} else if (Character.isUpperCase(name.charAt(i))) {
				newName = newName + " " + new String(new char[] { name.charAt(i) }).toLowerCase();
			} else {
				newName = newName + name.charAt(i);
			}
		}
		return newName;
	}

	public String getNameFieldName() throws Exception {
		if (BeanUtils.getPropertyDescriptor(this.getEntityClass(), "name") != null) {
			return "name";
		} else {
			return null;
		}
	};

	private GenericDao<T> dao;

	@SuppressWarnings("unchecked")
	public GenericDao<T> entityDao() throws Exception {
		return dao == null ? dao = (GenericDao<T>) BaseGeneric.beanOf(GenericDao.class, getEntityClass()) : dao;
	}
}
